package com.qk.sankuai.inf.leaf.snowflake;

import com.google.common.base.Preconditions;
import com.qk.sankuai.inf.IDGen;
import com.qk.sankuai.inf.leaf.common.Result;
import com.qk.sankuai.inf.leaf.common.Status;
import com.qk.sankuai.inf.leaf.common.Utils;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** @author daomingzhu */
public class SnowflakeIDGenImpl implements IDGen {

  @Override
  public boolean init() {
    return true;
  }

  private static final Logger LOGGER = LoggerFactory.getLogger(SnowflakeIDGenImpl.class);

  private final long twepoch;
  private static final long WORKER_ID_BITS = 10L;
  /** 最大能够分配的workerid =1023 */
  private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);

  private static final long SEQUENCE_BITS = 12L;
  private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
  private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
  private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
  private long workerId;
  private long sequence = 0L;
  private long lastTimestamp = -1L;
  private static final Random RANDOM = new Random();

  public SnowflakeIDGenImpl(String zkAddress, int port) {
    // Thu Nov 04 2010 09:42:54 GMT+0800 (中国标准时间)
    this(zkAddress, port, 1288834974657L);
  }

  /**
   * @param zkAddress zk地址
   * @param port snowflake监听端口
   * @param twepoch 起始的时间戳
   */
  public SnowflakeIDGenImpl(String zkAddress, int port, long twepoch) {
    this.twepoch = twepoch;
    Preconditions.checkArgument(
        timeGen() > twepoch, "Snowflake not support twepoch gt currentTime");
    final String ip = Utils.getIp();
    SnowflakeZookeeperHolder holder =
        new SnowflakeZookeeperHolder(ip, String.valueOf(port), zkAddress);
    LOGGER.info("twepoch:{} ,ip:{} ,zkAddress:{} port:{}", twepoch, ip, zkAddress, port);
    boolean initFlag = holder.init();
    if (initFlag) {
      workerId = holder.getWorkerID();
      LOGGER.info("START SUCCESS USE ZK WORKERID-{}", workerId);
    } else {
      Preconditions.checkArgument(initFlag, "Snowflake Id Gen is not init ok");
    }
    Preconditions.checkArgument(
        workerId >= 0 && workerId <= MAX_WORKER_ID, "workerID must gte 0 and lte 1023");
  }

  @Override
  public synchronized Result get(String key) {
    long timestamp = timeGen();
    if (timestamp < lastTimestamp) {
      long offset = lastTimestamp - timestamp;
      if (offset <= 5) {
        try {
          wait(offset << 1);
          timestamp = timeGen();
          if (timestamp < lastTimestamp) {
            return new Result(-1, Status.EXCEPTION);
          }
        } catch (InterruptedException e) {
          LOGGER.error("wait interrupted");
          return new Result(-2, Status.EXCEPTION);
        }
      } else {
        return new Result(-3, Status.EXCEPTION);
      }
    }
    if (lastTimestamp == timestamp) {
      sequence = (sequence + 1) & SEQUENCE_MASK;
      if (sequence == 0) {
        // seq 为0的时候表示是下一毫秒时间开始对seq做随机
        sequence = RANDOM.nextInt(100);
        timestamp = tilNextMillis(lastTimestamp);
      }
    } else {
      // 如果是新的ms开始
      sequence = RANDOM.nextInt(100);
    }
    lastTimestamp = timestamp;
    long id =
        ((timestamp - twepoch) << TIMESTAMP_LEFT_SHIFT) | (workerId << WORKER_ID_SHIFT) | sequence;
    return new Result(id, Status.SUCCESS);
  }

  protected long tilNextMillis(long lastTimestamp) {
    long timestamp = timeGen();
    while (timestamp <= lastTimestamp) {
      timestamp = timeGen();
    }
    return timestamp;
  }

  protected long timeGen() {
    return System.currentTimeMillis();
  }

  public long getWorkerId() {
    return workerId;
  }
}
